home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 52
/
Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso
/
Aminet
/
misc
/
emu
/
Apex-src.lha
/
ASM.XPL
< prev
next >
Wrap
Text File
|
2001-09-30
|
55KB
|
2,471 lines
\ASM.XPL APR-10-87 VERSION 1.0x10
\68000 ASSEMBLER
\Written by Loren Blaney for DFM Engineering
\REVISION HISTORY:
\APR-18-86, Original
\APR-30-86, Cross-reference
\MAY-03-86, INCLUDE pseudo op
\AUG-21-86, Fixed miscellaneous bugs
\SEP-08-86, Fixed LINK & STOP size bug
\OCT-19-86, Fixed sync problem with Bcc instrucitons, and modified for
\ new compiler.
\APR-10-87, Changed string conventions and fixed EXG An,An bug.
\WARNINGS:
\This assembler uses 32-bit constants and is intended to be compiled
\ using 32-bit XPL. However, it will work using 16-bit XPL if the program
\ you're assembling does not use any values outside the range +/-32K.
\ Set INTSIZE appropriately.
\
\This will not run on the 6502 without some modification of Apex-dependent
\ stuff. The INCLUDE pseudo-op requires this program to do its own input
\ buffering.
\
\NOTES:
\Beyond the mnemonic field, spaces are stripped out and have no meaning.
\It is assumed that a 96-column printer is used.
code ABS=0, REM=2, RESERVE=3, SWAP=4,
CHIN=7, CHOUT=8, CRLF=9, INTIN=10,
INTOUT=11, TEXT=12, OPENI=13, OPENO=14,
CLOSE=15, HEXOUT=27, SCAN=24, READ=31;
int CHAR, \Current character from GETCH
LINEINX, \Index into LINEBUF
LINECTR, \Count the the lines from start of (INCLUDE) file
HASH, \Current identifier hash code
OPCODE, \Code for first word of instruction
OPSIZE, \Instruction size in words
MNTYPE, \Mnemonic type (several opcodes belong to one type)
EA, \Effective address bit field (mode, register)
EXTLEN, \Number of extension words beyond opcode
EXTWD, \Array: instruction extension words
GENINX, \Index into GENBUF
MODEFLAG, \Bit array: indicates address mode
\Bit assignments:
\F: -- E: CCR D: SR C: USP
\B: Imm A: d(PC,Xn) 9: d(PC) 8: Abs.L
\7: Abs.W 6: d(An,Xn) 5: d(An) 4: -(An)
\3: (An)+ 2: (An) 1: An 0: Dn
PASS, \Assembly pass counter (1 or 2)
\(3 is a kludge to report some errors on pass 1)
ERRCNT, \Error counter
ERRTOLD, \Flag: used to report only one error per line
DEFAULT, \Array: default devices, etc.
LISTDEV, \Listing output device number
OBJDEV, \Object output device number (.OBJ file)
LISTON, \Flag: do listing
ASMON, \Flag: do assemble (for IF pseudo-op)
XREFON, \Flag: do cross reference
ENDFOUND, \Flag: END pseudo-op or EOF found
PC, \Program counter
LABEL, \Value of label field (usually = PC, = EXPR for EQU)
LABINX, \Symbol table index for label on current line
SYMNUM, \Index for next symbol in the symbol table
XREFNUM, \Index for next reference in cross-reference table
SYMVAL, \Array: symbol's value (PC, OPCODE, MODE)
SYMNEXT, \Array: linkage index to next entry with same hash code
HASHTBL, \Hash table: contains indexes into symbol table arrays
SYMINX, \Array: indicies sorted by symbol name
SYMXREF, \Array: indicies to cross-reference list
XREFPC, \Array: cross-reference table PC values
XREFNEXT, \Array: cross-reference table linkage index
INUNIT, \Unit that input file is on
FBLK, \First block of current input buffer-full
LBLK, \Last block of input file
INCLLEV, \Nesting level of current include file (0-7)
INBUFINX, \Index into INBUF
INCLNEST, \Array(8,5): parameters for each level of include nesting
INBUFSIZE, \Number of bytes in INBUF
II, \Scratch for MAIN
;
addr SYMTBL, \Array: symbol table identifier names (IDENT)
SYMCLASS, \Array: symbol class (label, mnemonic, or reserved)
SYMTYPE, \Array: symbol's type value --
\ for labels: normally = $00, changeable (EQU) = $01
\ for mnemonics: instruction type: NOP, JMP, ETC.
\ for reserved names: EA bits
LINEBUF, \Array: source code line buffer
IDENT, \Array: current identifier (symbol) name
GENBUF, \Array: holds object code (for listing)
INBUF, \Array: input buffer (same as Apex's)
ADDR, \Used to access bytes at absolute addresses
;
def TV=0, KB=0, NULLDEV=7; \I/O devices
def NUL=$00, BEL=$07, TAB=$09, SP=$20, \ASCII characters
CR=$0D, LF=$0A, FF=$0C,
EOF=$1A,
;
def INTSIZE=4, \number of bytes in an integer (2 or 4)
SYMSIZE=1000, \size of the symbol table (entries) *** DEBUG ***
XREFSIZE=5000, \size of cross-reference table (entries)
SIGCHAR=8, \no. of significant chars in IDENT
\SIGCHARS must be >= 8 because of INCLUDE
LINESIZE=256, \maximum number of characters allowed on a line
GENSIZE=6, \number of bytes of object code shown on listing
;
def \SYMCLASS\ LAB, MN, RES; \symbol class: label, mnemonic, reserved
def \MNTYPE\ NOP, JMP, MOVE, MOVEA, MOVEP, MOVEM, CLR, EXT, SWP, ADDI,
NBCD, EOR, EXG, LEA, LINK, STOP, TRAP, UNLK, ADD, CMP, CMPM, ADDA,
AND, ANDI, CHK, BCHG, ASL, BCC, ADDQ, MOVEQ, ABCD, ADDX, DBCC, SCC,
ORG, EQU, NOLIST, LIST, PAGE, DC, DS, DCB, ASCII, IF, ELSE, ENDIF,
INCLUDE, END;
\WARNING: ASMLINE assumes that NOP & SCC are respectively the first and
\ last non-pseudo instructions.
def \OPSIZE\ NOSIZE=0, BYTE=1, WORD=2, LONG=3, SHORT=4, BADSIZE=5;
def DEFOPSIZE=WORD; \DEFAULT OPCODE SIZE
\(BADSIZE means that size must be specified. I.e. don't allow ambiguous size)
\Apex system page parameters:
def INLBLK=$562, INHBLK=$566, INUNT=$56C, INBUFD=$48E, INBUFE=$492;
\========================= OUTPUT ROUTINES ============================
proc HEX_B(DEV, I); \Output ASCII hex byte to device
int DEV, I;
addr HEXDIGIT; \Array: ASCII hex DIGITS (0 - F)
begin
HEXDIGIT:= "0123456789ABCDEF";
CHOUT(DEV, HEXDIGIT(I>>4 & $0F));
CHOUT(DEV, HEXDIGIT(I & $0F));
end; \HEX_B
proc DOSYNC; \Generate a sync byte if necessary
begin
if PC & 1 then
begin
if PASS = 2 then HEX_B(OBJDEV,0); \output a sync byte
PC:= PC +1;
LABEL:= PC; \If there is a label then fix it
if LABINX # -1 then
begin
SYMVAL(LABINX):= LABEL;
SYMTYPE(LABINX):=$01; \Indicate changeable label (phase)
end;
end;
end; \DOSYNC
proc HEX0OUT(DEV,N); \Output ASCII hex integer
\This is similar to HEXOUT, but leading zeros are suppressed and the number
\ is right justified.
int DEV, N;
int FLAG, I, DIGIT;
addr HEXDIGIT, A;
begin
HEXDIGIT:= "0123456789ABCDEF";
A:=addr N;
FLAG:= 0; \Flag used to suppress leading zeros
for I:= 0,INTSIZE-1 do
begin
DIGIT:= A(I) >>4; \Output high nybble
FLAG:= FLAG ! DIGIT;
CHOUT(DEV,if FLAG then HEXDIGIT(DIGIT) else SP);
DIGIT:= A(I)&$0F; \Output low nybble
FLAG:= FLAG ! DIGIT ! I=INTSIZE-1;
CHOUT(DEV,if FLAG then HEXDIGIT(DIGIT) else SP);
end;
end; \HEX0OUT
proc GEN_B(OP); \Generate a byte of object code
int OP;
begin
if PASS = 2 then
begin
HEX_B(OBJDEV, OP);
if GENINX<GENSIZE then
[GENBUF(GENINX):= OP; GENINX:= GENINX +1];
end;
PC:= PC +1;
end; \GEN_B
proc GEN_W(OP); \Generate a word of object code
int OP;
begin
GEN_B(SWAP(OP)); GEN_B(OP);
end; \GEN_W
proc GENINSTR; \Generate object code for the current instruction
\Inputs: OPCODE, EXTLEN, EXTWD
int I;
begin
GEN_W(OPCODE);
for I:=1,EXTLEN do
begin
GEN_W(EXTWD(I-1));
end;
end; \GENINSTR
proc LINEOUT(DEV); \Output the line buffer to specified device
int DEV;
int I;
begin
for I:=0,LINESIZE-1 do
begin
CHOUT(DEV,LINEBUF(I));
if LINEBUF(I)=CR then I:=LINESIZE; \Exit 'FOR' loop
end;
end; \LINEOUT
proc LISTOUT(DEV); \List object code and LINEBUF on specified device
\Other inputs: MNTYPE, LABEL, GENBUF, GENINX
\012345678901234567890123
\01234567: 1234 12341234 LABEL MOVE.L
int DEV;
int I, FLAG;
begin
FLAG:= ASMON; \Should the label value be displayed?
case MNTYPE of
-1, NOLIST, LIST, PAGE, ELSE, ENDIF, ENDIF, INCLUDE, END:
if LABINX = -1 then FLAG:= false
other;
if FLAG then
begin
HEX0OUT(DEV,LABEL);
CHOUT(DEV,if GENINX>0 then ^: else SP);
CHOUT(DEV,SP);
for I:=0,GENSIZE-1 do
begin
if I=2 then CHOUT(DEV,SP);
if I<GENINX then HEX_B(DEV,GENBUF(I))
else TEXT(DEV," ");
end;
TEXT(DEV," ");
end
else TEXT(DEV," ");
LINEOUT(DEV);
end; \LISTOUT
\----------------------------------------------------------------------
proc QUICKSORT; \Quicksort SYMTBL into alphabetical order
\??? MULTIPLE LABELS ???
\Inputs: SYMTBL, SYMINX
\This routine is subtle and treacherous. You better understand it well
\ before dinging with it, or it will surly bomb.
int TEMP, PVTLINE;
int X, Y;
def SYMONE=0\$9E\; \Index of the first symbol beyond mnemonics, etc.
proc SORT(L, R);
int L, R; \Left and right limits to SYMINX array
int I, J; \Indicies
begin
loop begin
I:= L; J:= R;
PVTLINE:= SYMINX((L+R) >>1);
repeat begin
loop begin
X:= SYMINX(I); Y:= PVTLINE;
if X = Y then quit;
while SYMTBL(X) = SYMTBL(Y) do
[X:= X +SYMSIZE; Y:= Y +SYMSIZE];
if SYMTBL(X) > SYMTBL(Y) then quit;
I:= I +1;
end;
loop begin
X:= PVTLINE; Y:= SYMINX(J);
if X = Y then quit;
while SYMTBL(X) = SYMTBL(Y) do
[X:= X +SYMSIZE; Y:= Y +SYMSIZE];
if SYMTBL(X) > SYMTBL(Y) then quit;
J:= J -1;
end;
if I <= J then
begin \Swap elements
TEMP:= SYMINX(I);
SYMINX(I):= SYMINX(J);
SYMINX(J):= TEMP;
I:= I +1;
J:= J -1;
end;
end;
until I > J;
if J-L < R-I then
begin
if L < J then SORT(L, J);
if I < R then L:= I else quit; \SORT(I, R)
end
else begin
if I < R then SORT(I, R);
if L < J then R:= J else quit; \SORT(L, J)
end;
end;
end; \SORT
begin \QUICKSORT
if SYMNUM > SYMONE then SORT(SYMONE, SYMNUM-1);
end; \QUICKSORT
\----------------------------------------------------------------------
proc XREFOUT; \Output the cross-reference listing
\Inputs: SYMNUM, SYMCLASS, SYMTBL, SYMXREF, XREFNEXT, XREFPC.
int S, \Symbol
INX, \Index into symbol table
XINX, \Index into cross-reference table
FLAG, \Flag: indicates reference to a label
I, IINX, \Scratch
REF, \References-per-line counter
SPTR, \Stack pointer (index)
STACK; \Stack for reversing reference order
def MAXREF= 9, \Maximum number of references on a line (= 96 /9 -1)
STKSIZE= 1000; \Stack size (entries)
begin
STACK:= RESERVE(STKSIZE *INTSIZE);
CRLF(LISTDEV);
for I:=0,SYMNUM-1 do SYMINX(I):=I;
QUICKSORT; \Alphabetize the symbol names
for S:=0,SYMNUM-1 do \For all of the labels in the symbol table
begin
INX:= SYMINX(S); \Get the sorted index
if SYMCLASS(INX) = LAB then
begin
IINX:= INX; \List the symbol's name
for I:= 0,SIGCHAR-1 do
begin
CHOUT(LISTDEV,SYMTBL(IINX));
IINX:= IINX +SYMSIZE;
end;
CHOUT(LISTDEV,SP);
\Reverse the order of the references:
SPTR:= STKSIZE;
XINX:= SYMXREF(INX); \Get the index into the XREF table
loop begin
SPTR:= SPTR -1; \Push it onto the stack
if SPTR<0 then
begin
TEXT(TV,"*** XREF OVERFLOW ***");
CHOUT(TV,BEL);
return;
end;
STACK(SPTR):= XINX;
if XINX<0 then XINX:= -XINX;
XINX:= XREFNEXT(XINX);
if XINX= 0 then quit;
end;
REF:= 0; \List the references
loop begin
XINX:= STACK(SPTR);
SPTR:= SPTR +1; \Pull index from stack
FLAG:= XINX < 0; \Set flag if it is a label
if FLAG then XINX:= -XINX;
if REF >= MAXREF then
begin \New line -- indented
CRLF(LISTDEV);
for I:= 0,SIGCHAR do CHOUT(LISTDEV,SP);
REF:= 0;
end;
HEX0OUT(LISTDEV,XREFPC(XINX));
CHOUT(LISTDEV,if FLAG then ^. else SP);
REF:= REF +1;
if SPTR >= STKSIZE then quit;
end;
CRLF(LISTDEV);
end;
end;
end; \XREFOUT
\====================== MISCELLANEOUS ROUTINES ========================
func PEEKI(ADDR); \Returns the integer at the given address
int ADDR;
return ADDR(0);
proc ERROR(STR); \Output error message to TV and LISTDEV
int STR;
begin
if PASS = 1 then return; \Don't report any errors on pass 1
\("Undefineds" may be forward references; also, forward referenced values
\ may be out of range)
if ERRTOLD then return; \Only report one error per line
TEXT(TV,"***** ERROR ON LINE "); INTOUT(TV,LINECTR); TEXT(TV," - ");
TEXT(TV,STR);
CRLF(TV);
if LISTDEV#TV ! not LISTON ! PASS=3 then LISTOUT(TV); \Make sure line is shown
if LISTDEV#TV then \(Don't display it a second time)
begin
TEXT(LISTDEV,"***** ERROR ON LINE "); INTOUT(LISTDEV,LINECTR);
TEXT(LISTDEV," - ");
TEXT(LISTDEV,STR);
CRLF(LISTDEV);
if not LISTON ! PASS = 3 then LISTOUT(LISTDEV); \Make sure line is shown
end;
ERRCNT:= ERRCNT+1;
ERRTOLD:= true;
end; \ERROR
\----------------------------------------------------------------------
func SWAPWD(I); \Swap words in a 32-bit integer
int I;
addr A;
int T;
begin
A:=addr I;
T:=A(0); A(0):= A(2); A(2):= T;
T:=A(1); A(1):= A(3); A(3):= T;
return I;
end; \SWAPWD
\----------------------------------------------------------------------
proc CK08(VAL); \Check for overflow of an 8-bit value
int VAL;
begin
if VAL>127 ! VAL<-128 then ERROR("OVERFLOW");
end; \CK08
proc CK08U(VAL); \Check for overflow of a (possibly unsigned) 8-bit value
int VAL;
begin
if VAL>255 ! VAL<-128 then ERROR("OVERFLOW");
end; \CK08U
proc CK16(VAL); \Check for overflow of a 16-bit value
int VAL;
begin
if INTSIZE=4 then
if VAL>32767 ! VAL<-32768 then ERROR("OVERFLOW");
end; \CK16
proc CK16U(VAL); \Check for overflow of a (possibly unsigned) 16-bit value
int VAL;
begin
if INTSIZE=4 then
if VAL>=$10000 ! VAL<-32768 then ERROR("OVERFLOW");
end; \CK16
\----------------------------------------------------------------------
func LOOKUP(CLASS); \Lookup current identifier and return its index
\Other inputs: IDENT, HASH;
\Outputs: INDEX OF IDENTIFIER. RETURNS -1 IF NOT FOUND.
int CLASS; \Class of symbol (LAB, MN, RES)
int I, K, INX;
begin
INX:= HASHTBL(HASH);
loop begin
if INX= -1 then quit; \Not found
I:= 0; K:= INX;
while IDENT(I)=SYMTBL(K) & I<SIGCHAR do
[I:= I+1; K:= K+SYMSIZE];
if I=SIGCHAR & SYMCLASS(INX)=CLASS then quit; \Found
INX:= SYMNEXT(INX); \Follow the linkage pointers
end;
return INX;
end; \LOOKUP
proc INSERT(CLASS, VAL, TYPE);
\**** ???? reverse insertion order for maximum efficiency ????
\Insert the current identifier into the symbol table
\Other inputs: IDENT, HASH;
int CLASS, VAL, TYPE;
int I, INX, K, SPASS;
begin
INX:= LOOKUP(CLASS);
if INX # -1 then
begin
SPASS:= PASS; PASS:= 3; \Kludge to report error on pass 1
ERROR("LABEL ALREADY USED");
PASS:= SPASS;
XREFON:= false; \(XREF can't handle multiple labels)
end;
if SYMNUM>=SYMSIZE then
begin
SPASS:= PASS; PASS:= 3; \Kludge to report error on pass 1
ERROR("SYMBOL TABLE OVERFLOW");
PASS:= SPASS;
SYMNUM:= SYMSIZE-1;
end;
K:= SYMNUM;
for I:= 0,SIGCHAR-1 do
[SYMTBL(K):= IDENT(I); K:= K+SYMSIZE];
SYMCLASS(SYMNUM):= CLASS;
SYMVAL(SYMNUM):= VAL;
SYMTYPE(SYMNUM):= TYPE;
SYMNEXT(SYMNUM):= HASHTBL(HASH); \Link back
HASHTBL(HASH):= SYMNUM;
SYMNUM:= SYMNUM+1;
end; \INSERT
proc DOXREF(I, FLAG); \Record reference to symbol "I"
int I, FLAG; \Flag to indicate a label definition
begin
if PASS = 2 then
begin
if XREFNUM >= XREFSIZE then
begin
ERROR("CROSS-REFERENCE OVERFLOW");
XREFON:=false;
end;
if XREFON then
begin
XREFPC(XREFNUM):= PC;
XREFNEXT(XREFNUM):= SYMXREF(I); \Link in reference
SYMXREF(I):= XREFNUM;
if FLAG then SYMXREF(I):= -XREFNUM;
XREFNUM:= XREFNUM +1;
end;
end;
end; \DOXREF
\========================== INPUT ROUTINES ============================
proc OPENIN; \Initialize source device as a level-0 include file
begin
\Get the parameters that were set up by Apex
INUNIT:= ADDR(INUNT); \Get the input unit number
INBUF:= PEEKI(INBUFD); \Get the input buffer
INBUFSIZE:= PEEKI(INBUFE) - INBUF;
FBLK:= PEEKI(INLBLK); \Get the first and last blocks
LBLK:= PEEKI(INHBLK);
INCLLEV:= 0; \Start at the first nesting level
INBUFINX:= INBUFSIZE; \Indicate that the buffer is empty
end; \OPENIN
func CHINX; \Get a character from the input file
\(This is optimized for speed)
int CH;
proc READBUF; \Read another buffer-full from (INCLUDE) file
int NBLK, \Total number of blocks in INBUF
N; \Number of blocks actually read into buffer
begin
NBLK:= INBUFSIZE >>8;
N:= if FBLK+NBLK > LBLK then LBLK -FBLK +1 else NBLK;
READ(INUNIT, FBLK, INBUF, N);
FBLK:= FBLK +NBLK; \Point to next buffer-full
end; \READBUF (Use NBLK not N because of UNNEST)
proc UNNEST; \Unnest one include file level
int NBLK; \Total number of blocks in INBUF
begin
INCLLEV:= INCLLEV -1; \Unnest one level
FBLK:= INCLNEST(INCLLEV, 0); \Restore parameters
LBLK:= INCLNEST(INCLLEV, 1);
INBUFINX:= INCLNEST(INCLLEV, 2);
INUNIT:= INCLNEST(INCLLEV, 3);
LINECTR:= INCLNEST(INCLLEV, 4);
NBLK:= INBUFSIZE >>8; \Back up FBLK
FBLK:= FBLK -NBLK;
READBUF;
end; \UNNEST
begin \CHINX
if INBUFINX >= INBUFSIZE then [INBUFINX:= 0; READBUF];
CH:= INBUF(INBUFINX);
INBUFINX:= INBUFINX +1;
if CH = EOF then
if INCLLEV > 0 then
begin
UNNEST; \If INCLLEV = 0 then it's a hard EOF
CH:= CHINX;
end;
return CH;
end; \CHINX
proc GETLINE; \Read a line of source code into LINEBUF
\Sets LINEINX to start of line (optimized for speed)
int CH, I;
begin
loop begin
for I:=0,LINESIZE-1 do
begin
CH:= CHINX;
case CH of
CR: [LINEBUF(I):= CR; quit];
EOF: [ENDFOUND:= true; LINEBUF(I):= CR; quit]
other LINEBUF(I):= CH;
end;
\The line is too long (I = LINESIZE)
LINEBUF(LINESIZE-1):=CR; \Terminate the line
repeat CH:=CHINX until CH=CR ! CH=EOF; \Eat the rest of the line
if CH = EOF then ENDFOUND:= true;
quit;
end;
LINEINX:= 0; \Set index to start of buffer
LINECTR:= LINECTR +1;
end; \GETLINE
\----------------------------------------------------------------------
proc GETCH; \Get a character from LINEBUF, convert to uppercase
begin
CHAR:= LINEBUF(LINEINX);
if CHAR>=^a then
if CHAR<=^z then CHAR:=CHAR-$20; \shift to uppercase
LINEINX:= LINEINX +1;
end; \GETCH
proc SKIPTAB; \Skip tabs and spaces in LINEBUF
begin
while CHAR=TAB ! CHAR=SP do GETCH;
end; \SKIPTAB
proc GETCHX; \Get next character, ignoring any space characters
begin
repeat GETCH until CHAR#SP;
end; \GETCHX
proc GETCOMMA; \Get a comma
begin
if CHAR#^, then ERROR("COMMA EXPECTED");
GETCHX; \Skip the comma
end; \GETCOMMA
proc GETIDENT; \Read in an identifier name, e.g: FROG
\OUTPUTS: HASH, IDENT
int LEN;
begin
IDENT(0):= CHAR;
HASH:= CHAR;
GETCH;
LEN:= 1;
loop case of
CHAR>=^A & CHAR<=^Z, CHAR>=^0 & CHAR<=^9, CHAR=^_ :
begin
if LEN <SIGCHAR then
begin
IDENT(LEN):= CHAR;
HASH:= HASH +CHAR;
LEN:= LEN +1;
end;
GETCH;
end
other quit;
for LEN:= LEN,SIGCHAR-1 do
[IDENT(LEN):= SP; HASH:= HASH +SP];
HASH:= HASH & $FF;
end; \GETIDENT
func GETDEC; \Read in a decimal string and return its value, e.g: 123
\The first digit is already read in, the first non-digit character (terminator)
\ will be in CHAR
int VAL;
begin
VAL:= CHAR -^0;
GETCH;
loop begin
if CHAR<^0 ! CHAR>^9 then quit;
VAL:= VAL *10 +CHAR -^0;
GETCH;
end;
return VAL;
end; \GETDEC
func GETHEX; \Return the value of a hex string, e.g: $1AC4
int VAL, I;
begin
GETCH; \Skip the "$"
case of
CHAR>=^0 & CHAR<=^9: VAL:= CHAR-^0;
CHAR>=^A & CHAR<=^F: VAL:= CHAR-$37
other [ERROR("HEX DIGIT EXPECTED"); return 0];
loop begin
GETCH;
case of
CHAR>=^0 & CHAR<=^9: I:= CHAR-^0;
CHAR>=^A & CHAR<=^F: I:= CHAR-$37
other quit;
if VAL>$FFFFFFF then ERROR("OVERFLOW");
VAL:= VAL <<4 +I;
end;
return VAL;
end; \GETHEX
func GETSTR; \Return the value of a string, e.g: "ABC"
int VAL, I, QUOTE;
begin
VAL:= 0;
I:= 0;
QUOTE:= CHAR; \save the starting quote mark
loop begin
CHAR:= LINEBUF(LINEINX); \(don't shift to upper case)
LINEINX:= LINEINX +1;
if CHAR = QUOTE then [GETCH; quit];
if CHAR=CR then [ERROR("QUOTE MISSING"); quit];
I:= I +1;
if I = INTSIZE+1 then ERROR("OVERFLOW");
VAL:= VAL <<8 + CHAR;
end;
return VAL;
end; \GETSTR
\======================== EXPRESSION EVALUATOR ========================
func EXPRESSION; \Returns the value of an expression
int VAL;
func FACTOR; \Returns the value of a factor
int VAL, I, NEG;
begin
while CHAR = ^+ do GETCHX; \Ignore unary "+"
NEG:=false;
while CHAR =^- do \Unary "-"
[GETCHX; NEG:=not NEG];
if CHAR>=^A & CHAR<=^Z then \Label name
begin
GETIDENT;
I:= LOOKUP(RES);
if I # -1 then ERROR("RESERVED NAME")
else begin
I:= LOOKUP(LAB);
VAL:= 0; \Set default value
if I = -1 then ERROR("UNDEFINED")
else begin
VAL:= SYMVAL(I);
DOXREF(I,false);
end;
end;
end
else if CHAR>=^0 & CHAR<=^9 then \Unsigned integer
VAL:= GETDEC
else case CHAR of
^[: begin \Subexpression
GETCHX;
VAL:= EXPRESSION;
if CHAR#^] then ERROR("^"]^" EXPECTED");
GETCHX;
end;
^@: begin
VAL:= PC; \Program counter
GETCHX; \Eat the "@"
end;
^$: VAL:=GETHEX; \Unsigned hex integer
^",^': VAL:=GETSTR \String constant
other ERROR("SYNTAX");
SKIPTAB; \Identifiers, numbers, etc. may terminate with a space or tab
return if NEG then -VAL else VAL;
end; \FACTOR
func TERM; \Returns FACTOR * FACTOR, etc.
int VAL;
begin
VAL:= FACTOR;
loop case CHAR of
^*: [GETCHX; VAL:= VAL * FACTOR];
^/: [GETCHX; VAL:= VAL / FACTOR];
^\: [GETCHX; VAL:= REM(VAL / FACTOR)] \Modulo
other quit;
return VAL;
end; \TERM
proc ALGEXP; \Returns TERM + TERM, etc.
int VAL;
begin
VAL:= TERM;
loop case CHAR of
^+: [GETCHX; VAL:= VAL + TERM];
^-: [GETCHX; VAL:= VAL - TERM]
other quit;
return VAL;
end; \ALGEXP
func LOGEXP; \Returns a boolean, e.g: ALGEXP < ALGEXP
int VAL;
begin
if CHAR=^~ then \Unary 'NOT' operator
begin
GETCHX;
VAL:= not LOGEXP;
end
else begin
VAL:= ALGEXP;
case CHAR of
^=: [GETCHX; VAL:= VAL = ALGEXP];
^#: [GETCHX; VAL:= VAL # ALGEXP];
^>: begin
GETCHX; if CHAR#^= then VAL:= VAL > ALGEXP
else [GETCHX; VAL:= VAL >= ALGEXP];
end;
^<: begin
GETCHX; if CHAR#^= then VAL:= VAL < ALGEXP
else [GETCHX; VAL:= VAL <= ALGEXP];
end
other;
end;
return VAL;
end; \LOGEXP
func BOOLTERM; \Returns LOGEXP & LOGEXP
int VAL;
begin
VAL:= LOGEXP;
loop begin
if CHAR=^& then
[GETCHX; VAL:= VAL & LOGEXP]
else quit;
end;
return VAL;
end; \BOOLTERM
begin \EXPRESSION
SKIPTAB; \Must not have a tab or space
VAL:= BOOLTERM;
loop begin
if CHAR=^! then
[GETCHX; VAL:= VAL ! BOOLTERM]
else quit;
end;
return VAL;
end; \EXPRESSION
\==================== EFFECTIVE ADDRESS GENERATOR =====================
proc DOEA(OKMODE); \Get effective address mode bits
\OUTPUTS:
\ EA, Effective address bit field (mode & reg bits)
\ EXTLEN, Number of extension words beyond opcode
\ EXTWD, Array: instruction extension words
\ MODEFLAG, Bit array indicating address mode
int OKMODE; \Bit array of ok EA modes (see MODEFLAG definitions)
int VAL, \Value returned by EXPRESSION
I,
SLINEINX, \Temporary save of LINEINX and CHAR
SCHAR;
proc DOAREG; \Get address register, return register bits in EA
int I;
begin
GETIDENT;
SKIPTAB;
I:= LOOKUP(RES);
EA:= SYMTYPE(I);
if I= -1 ! EA<$8 ! EA>$F then
ERROR("^"A^" REGISTER EXPECTED");
EA:= EA & $7;
end; \DOAREG
\----------------------------------------------------------------------
proc EXPRCASE; \EA starting with an expression, e.g: d(An), d(PC,Xi)
int I;
proc DOINDEX; \Parse index register, return extension word
int I, R;
begin
GETIDENT;
SKIPTAB;
I:= LOOKUP(RES);
R:= SYMTYPE(I);
if I=-1 ! R>$0F then ERROR("REGISTER EXPECTED");
EXTWD(EXTLEN-1):= R <<12;
if CHAR=^. then \Size field
begin
GETCHX;
case CHAR of
^W: begin
GETCHX;
end;
^L: begin
GETCHX;
EXTWD(EXTLEN-1):= EXTWD(EXTLEN-1) ! $0800;
end
other ERROR("INDEX SIZE");
end;
\assume size = word if not specified
CK08(VAL);
EXTWD(EXTLEN-1):= EXTWD(EXTLEN-1) ! (VAL & $FF);
end; \DOINDEX
proc ABSLONG; \EA = absolute long
begin
EA:= $39;
MODEFLAG:= $0100;
EXTLEN:= EXTLEN +1;
EXTWD(EXTLEN-2):= SWAPWD(VAL); \High word
EXTWD(EXTLEN-1):= VAL; \Low word
end; \ABSLONG
proc ABSWORD; \EA = absolute word
begin
EA:= $38;
MODEFLAG:= $0080;
CK16(VAL);
EXTWD(EXTLEN-1):= VAL;
end; \ABSWORD
begin \EXPRCASE
VAL:= EXPRESSION;
EXTLEN:= EXTLEN +1; \Assume there is one extension word
case CHAR of
^(: begin
GETCHX;
case CHAR of
^A,^S: begin \An or SP
DOAREG;
if CHAR#^, then
begin \d(An)
EA:= EA + $28;
MODEFLAG:= $0020;
CK16(VAL);
EXTWD(EXTLEN-1):= VAL;
end
else begin \d(An,Xn)
EA:= EA + $30;
MODEFLAG:= $0040;
GETCHX; \Eat the comma
DOINDEX;
end;
end;
^P: begin
GETCHX;
if CHAR#^C then ERROR("^"PC^" EXPECTED");
GETCHX;
if CHAR#^, then \d(PC)
begin
EA:= $3A;
MODEFLAG:= $0200;
CK16(VAL);
EXTWD(EXTLEN-1):= VAL;
end
else begin \d(PC,Xn)
EA:= $3B;
MODEFLAG:= $0400;
GETCHX; \Eat the comma
DOINDEX;
end;
end
other ERROR("An OR PC EXPECTED");
if CHAR#^) then ERROR("^")^" EXPECTED");
GETCHX;
end;
^.: begin \Get size field
GETCHX; \Skip the "."
case CHAR of
^W: begin \Abs.W
GETCHX; \Skip the "W"
ABSWORD;
end;
^L: begin \Abs.L
GETCHX; \Skip the "L"
ABSLONG;
end
other ERROR("ABS. SIZE");
end
other ABSWORD; \Assume size = word
end; \EXPRCASE
\----------------------------------------------------------------------
begin \DOEA
SKIPTAB; \Tabs and spaces are stripped out beyond this point
case CHAR of
^(: begin \(An) or (An)+
GETCHX; \Get next char, stripping out tabs and spaces
DOAREG;
if CHAR#^) then ERROR("^")^" EXPECTED");
GETCHX;
if CHAR#^+ then
begin \(An)
EA:= EA + $10;
MODEFLAG:= $0004;
end
else begin \(An)+
GETCHX;
EA:= EA + $18;
MODEFLAG:= $0008;
end;
end;
^-: begin \-(An)
if LINEBUF(LINEINX)=^( then
begin
GETCH; GETCHX; \Skip the "-("
DOAREG;
if CHAR#^) then ERROR("^")^" EXPECTED");
GETCHX;
EA:= EA + $20;
MODEFLAG:= $0010;
end
else EXPRCASE;
end;
^#: begin \#<xxx>
GETCHX;
VAL:= EXPRESSION;
EA:= $3C;
MODEFLAG:= $0800;
case OPSIZE of
BYTE: begin \.B
EXTLEN:= EXTLEN +1;
CK08U(VAL);
EXTWD(EXTLEN-1):= VAL & $00FF;
end; \(Beware of extended negative values)
WORD: begin \.W
EXTLEN:= EXTLEN +1;
CK16U(VAL);
EXTWD(EXTLEN-1):= VAL;
end;
LONG: begin \.L
EXTLEN:= EXTLEN +2;
EXTWD(EXTLEN-2):= SWAPWD(VAL); \High word
EXTWD(EXTLEN-1):= VAL; \Low word
end
other ERROR("SIZE"); \(Should already be detected by EA mode)
end
other begin
\Save current state in case it's not a reserved name:
SLINEINX:= LINEINX; SCHAR:= CHAR;
GETIDENT;
SKIPTAB;
I:= LOOKUP(RES);
if I # -1 then
begin \Reserved name:
MODEFLAG:= SYMVAL(I); \ D0-D7, A0-A7, SP, USP, CCR, SR
EA:= SYMTYPE(I);
if OPSIZE=BYTE & EA>=$08 & EA<=$0F then ERROR("SIZE");
end \(Can't use bytes with address regs)
else begin \EXPRESSION
LINEINX:= SLINEINX; CHAR:= SCHAR;
EXPRCASE;
end;
end;
if MODEFLAG & not OKMODE then ERROR("ADDRESS MODE");
end; \DOEA
\----------------------------------------------------------------------
proc DOSIZE(DEFSIZE, OKSIZE); \Get opcode size and check if legal
int DEFSIZE, \Default size
OKSIZE, \Bit array of ok sizes:
\ $10=BAD, $08=SHORT, $04=LONG, $02=WORD, $01=BYTE
TBL; \Table to convert OPSIZE to corresponding bit
begin
OPSIZE:= DEFSIZE;
if CHAR=^. then
begin
GETCH; \Get character immediately following the "."
case CHAR of
^B: OPSIZE:= BYTE;
^W: OPSIZE:= WORD;
^L: OPSIZE:= LONG;
^S: OPSIZE:= SHORT
other OPSIZE:=BADSIZE; \(Because we have a ".")
GETCH; \Skip size character
end;
TBL:= [$0, $1, $2, $4, $8, $10]; \NOSIZE, BYTE, WORD, LONG, SHORT, BADSIZE
if TBL(OPSIZE) & not OKSIZE then ERROR("SIZE");
\Illegal size was specified (or internal problem)
end; \DOSIZE
\========================= OPCODE HANDLERS ============================
proc DONOP; \NOP
begin
DOSIZE(NOSIZE, $0);
GENINSTR;
end; \DONOP
proc DOJMP; \JMP <EA>
begin
DOSIZE(NOSIZE, $0);
DOEA($07E4);
OPCODE:= OPCODE ! EA;
GENINSTR;
end; \DOJMP
proc DOMOV; \MOVE._ <EA>,<EA>
int R, M, TBL, SEA;
begin
DOSIZE(DEFOPSIZE, $7);
DOEA($3FFF);
case MODEFLAG of
$2000: begin \From SR
if OPSIZE # WORD then ERROR("SIZE");
GETCOMMA;
DOEA($01FD);
OPCODE:= $40C0 ! EA;
GENINSTR;
end;
$1000: begin \from USP
OPSIZE:= LONG; \Warning: Illegal size not checked for
GETCOMMA;
DOEA($0002);
OPCODE:= $4E68 ! (EA&$7);
GENINSTR;
end
other begin
OPCODE:= OPCODE ! EA;
SEA:= EA;
GETCOMMA;
DOEA($71FD);
case MODEFLAG of
$4000: begin \To CCR
if OPSIZE # WORD then ERROR("SIZE");
if (SEA&$38) = $08 then ERROR("ADDRESS MODE"); \An is illegal
OPCODE:= $44C0 ! SEA;
GENINSTR;
end;
$2000: begin \To SR
if OPSIZE # WORD then ERROR("SIZE");
if (SEA&$38) = $08 then ERROR("ADDRESS MODE"); \An is illegal
OPCODE:= $46C0 ! SEA;
GENINSTR;
end;
$1000: begin \From USP
OPSIZE:= LONG; \Warning: Illegal size not checked for
if (SEA&$38) # $08 then ERROR("ADDRESS MODE"); \Must be An
OPCODE:= $4E60 ! (SEA&$7);
GENINSTR;
end
other begin
R:= EA & $0007; \Get the destination register and mode bits
M:= EA & $0038;
EA:= R <<9 + M <<3;
TBL:= [$0, $1000, $3000, $2000]; \NOSIZE, BYTE, WORD, LONG
OPCODE:= OPCODE ! EA ! TBL(OPSIZE);
GENINSTR;
end;
end;
end; \DOMOV
proc DOMOVA; \MOVEA._ <EA>,An
int R, M, TBL;
begin
DOSIZE(DEFOPSIZE, $6); \Word or long
DOEA($0FFF);
OPCODE:= OPCODE ! EA;
GETCOMMA;
DOEA($0002);
R:= EA & $0007; \Get the destination register and mode bits
M:= EA & $0038;
EA:= R <<9 + M <<3;
TBL:= [$0, $1000, $3000, $2000]; \NOSIZE, BYTE, WORD, LONG
OPCODE:= OPCODE ! EA ! TBL(OPSIZE);
GENINSTR;
end; \DOMOVA
proc DOMOVP; \MOVEP._ Dn,d(An) MOVEP._ d(An),Dn
begin
DOSIZE(DEFOPSIZE, $6); \Word or long
DOEA($0021);
GETCOMMA;
if MODEFLAG=$0001 then
begin \Source is a data register
OPCODE:= OPCODE ! EA <<9 ! OPSIZE <<6;
DOEA($0020);
OPCODE:= OPCODE ! (EA &$07);
end
else begin \Source is memory
OPCODE:= OPCODE ! (OPSIZE-2) <<6 ! (EA &$07);
DOEA($0001);
OPCODE:= OPCODE ! EA <<9;
end;
GENINSTR;
end; \DOMOVP
proc DOMOVM; \MOVEM._ D3/D5-D7/A3,<ea>
int BIT, MASK; \MOVEM._ <ea>,D3/D5-D7/A3
func REVERSE(N); \Reverse the order of the bits in "N"
int N;
int I, M;
begin
M:=0;
for I:=0,15 do
if BIT(I) & N then M:=M ! BIT(15-I);
return M;
end; \REVERSE
proc REGLIST; \E.G: D1/D3-D5/A2-A4/A6
int I, SEA;
begin \First reg is already in MASK
loop begin
case CHAR of
^/: begin
GETCHX;
DOEA($0003);
MASK:= MASK ! BIT(EA);
end;
^-: begin
GETCHX;
SEA:= EA;
DOEA($0003);
if EA<=SEA then ERROR("ADDRESS MODE");
for I:=SEA,EA do MASK:= MASK ! BIT(I);
end
other quit;
end;
end; \REGLIST
begin \DOMOVM
BIT:= [ $0001, $0002, $0004, $0008, $0010, $0020, $0040, $0080,
$0100, $0200, $0400, $0800, $1000, $2000, $4000, $8000];
DOSIZE(DEFOPSIZE, $6); \Word or long
if OPSIZE=LONG then OPCODE:= OPCODE ! $40;
EXTLEN:= 1; \Reserve space for register mask
DOEA($07EF);
if MODEFLAG & $0003 then
begin \MOVEM._ D3/D5-D7/A3,<ea>
MASK:= BIT(EA);
REGLIST;
GETCOMMA;
DOEA($01F4);
OPCODE:= OPCODE ! EA;
if MODEFLAG=$0010 then \If predecrement mode, reverse MASK bits
MASK:= REVERSE(MASK);
end
else begin \MOVEM._ <ea>,D3/D5-D7/A3
OPCODE:= OPCODE ! $400 ! EA;
GETCOMMA;
DOEA($0003); \Get the first register
MASK:= BIT(EA);
REGLIST;
end;
EXTWD(0):= MASK;
GENINSTR;
end; \DOMOVM
proc DOCLR; \CLR._ <EA>
begin
DOSIZE(DEFOPSIZE, $7);
DOEA($01FD);
OPCODE:= OPCODE ! EA ! (OPSIZE-1) <<6;
GENINSTR;
end; \DOCLR
proc DOEXT; \EXT._ Dn
begin
DOSIZE(DEFOPSIZE, $6); \Word or long
DOEA($0001);
OPCODE:= OPCODE ! EA ! OPSIZE <<6;
GENINSTR;
end; \DOEXT
proc DOSWAP; \SWAP.W Dn
begin
DOSIZE(WORD, $2);
DOEA($0001);
OPCODE:= OPCODE ! EA;
GENINSTR;
end; \DOSWAP
proc DOADDI; \ADDI._ #<xxx>,<EA>
begin
DOSIZE(DEFOPSIZE, $7);
DOEA($0800);
GETCOMMA;
DOEA($01FD);
OPCODE:= OPCODE ! EA ! (OPSIZE-1) <<6;
GENINSTR;
end; \DOADDI
proc DONBCD; \NBCD <EA>
begin
DOSIZE(BYTE, $1);
DOEA($01FD);
OPCODE:= OPCODE ! EA;
GENINSTR;
end; \DONBCD
proc DOEOR; \EOR._ Dn,<EA>
begin
DOSIZE(DEFOPSIZE, $7);
DOEA($0001);
OPCODE:= OPCODE ! EA <<9;
GETCOMMA;
DOEA($01FD);
OPCODE:= OPCODE ! EA ! (OPSIZE-1) <<6;
GENINSTR;
end; \DOEOR
proc DOEXG; \EXG.L Rn,Rn
int SEA;
begin
DOSIZE(LONG, $4);
DOEA($0003);
SEA:= EA;
GETCOMMA;
DOEA($0003);
\If address register was given first...
OPCODE:= OPCODE ! (if SEA >=8 then (EA&7) <<9 ! SEA else SEA <<9 ! EA);
OPCODE:= OPCODE ! (if (SEA | EA) &8 then \An & Dn\ $80 else $40);
GENINSTR;
end; \DOEXG
proc DOLEA; \LEA.L <EA>,An
begin
DOSIZE(LONG, $4);
DOEA($07E4);
OPCODE:= OPCODE ! EA;
GETCOMMA;
DOEA($0002);
OPCODE:= OPCODE ! (EA&$7) <<9;
GENINSTR;
end; \DOLEA
proc DOLINK; \LINK An,#<displacement>
begin
DOSIZE(NOSIZE, $0);
DOEA($0002);
OPCODE:= OPCODE ! (EA&$7);
GETCOMMA;
OPSIZE:= WORD; \Force to size word for #<xxx>
DOEA($0800);
GENINSTR;
end; \DOLINK
proc DOSTOP; \STOP #<xxx>
begin
DOSIZE(NOSIZE, $0);
OPSIZE:= WORD; \Force to size word for #<xxx>
DOEA($0800);
GENINSTR;
end; \DOSTOP
proc DOTRAP; \TRAP #<vector>
int VAL;
begin
DOSIZE(NOSIZE, $0);
SKIPTAB;
if CHAR#^# then ERROR("SYNTAX");
GETCHX;
VAL:= EXPRESSION;
if VAL>15 ! VAL<0 then ERROR("OVERFLOW")
else OPCODE:= OPCODE ! VAL;
GENINSTR;
end; \DOTRAP
proc DOUNLK; \UNLK An
begin
DOSIZE(NOSIZE, $0);
DOEA($0002);
OPCODE:= OPCODE ! (EA&$7);
GENINSTR;
end; \DOUNLK
proc DOADD; \ADD._ <EA>,Dn ADD._ Dn,<EA>
int D;
begin
DOSIZE(DEFOPSIZE, $7);
OPCODE:= OPCODE ! (OPSIZE-1) <<6;
DOEA($0FFF);
GETCOMMA;
if MODEFLAG=$0001 then
begin \Source is a data register
D:= EA; \Save the data register number
DOEA($01FD); \If the destination is a data register
if MODEFLAG=$0001 then \ then we must use destination Dn mode
OPCODE:= OPCODE ! EA<<9 +D
else OPCODE:= OPCODE ! D <<9 ! EA ! $100;
end
else begin \Source is not a data register
OPCODE:= OPCODE ! EA;
DOEA($0001); \Destination must be a data register
OPCODE:= OPCODE ! EA <<9;
end;
GENINSTR;
end; \DOADD
proc DOCMP; \CMP._ <EA>,Dn
begin
DOSIZE(DEFOPSIZE, $7);
OPCODE:= OPCODE ! (OPSIZE-1) <<6;
DOEA($0FFF);
GETCOMMA;
OPCODE:= OPCODE ! EA;
DOEA($0001); \Destination must be a data register
OPCODE:= OPCODE ! EA <<9;
GENINSTR;
end; \DOCMP
proc DOCMPM; \CMPM._ (Ay)+,(Ax)+
begin
DOSIZE(DEFOPSIZE, $7);
DOEA($0008);
OPCODE:= OPCODE ! (EA&$7) ! (OPSIZE-1) <<6;
GETCOMMA;
DOEA($0008);
OPCODE:= OPCODE ! (EA&$7) <<9;
GENINSTR;
end; \DOCMPM
proc DOADDA; \ADDA._ <ea>,An
begin
DOSIZE(DEFOPSIZE, $6); \Word or long
OPCODE:= OPCODE ! (if OPSIZE=WORD then $0C0 else $1C0);
DOEA($0FFF);
OPCODE:= OPCODE ! EA;
GETCOMMA;
DOEA($0002);
OPCODE:= OPCODE ! (EA&$7) <<9;
GENINSTR;
end; \DOADDA
proc DOAND; \AND._ <ea>,Dn AND._ Dn,<ea>
int D;
begin
DOSIZE(DEFOPSIZE, $7);
OPCODE:= OPCODE ! (OPSIZE-1) <<6;
DOEA($0FFD);
GETCOMMA;
if MODEFLAG=$0001 then
begin \Source is a data register
D:= EA; \Save the data register number
DOEA($01FD); \If the destination is a data register
if MODEFLAG = $0001 then \ then we must use destination Dn mode
OPCODE:= OPCODE ! EA<<9 +D
else OPCODE:= OPCODE ! D <<9 ! EA ! $100;
end
else begin \Source is not a data register
OPCODE:= OPCODE ! EA;
DOEA($0001);
OPCODE:= OPCODE ! EA <<9;
end;
GENINSTR;
end; \DOAND
proc DOANDI; \ANDI._ #<xxx>,<ea>
begin
DOSIZE(DEFOPSIZE, $7);
DOEA($0800); \Immediate mode
GETCOMMA;
DOEA($61FD);
OPCODE:= OPCODE ! EA;
if MODEFLAG <= $100 then
OPCODE:= OPCODE ! (OPSIZE-1) <<6
else if MODEFLAG = $4000 then \ANDI.B #<xxx>,CCR
if OPSIZE#BYTE then ERROR("SIZE")
else if MODEFLAG = $2000 then \ANDI.W #<xxx>,SR
if OPSIZE#WORD then ERROR("SIZE");
GENINSTR;
end; \DOANDI
proc DOCHK; \CHK.W <ea>,Dn
begin
DOSIZE(WORD, $2);
DOEA($0FFD);
OPCODE:= OPCODE ! EA;
GETCOMMA;
DOEA($0001);
OPCODE:= OPCODE ! EA <<9;
GENINSTR;
end; \DOCHK
proc DOBCHG;
\BCHG.L Dn,Dn BCHG.B Dn,<ea> dynamic
\BCHG.L #<data>,Dn BCHG.B #<data>,<ea> static
\Warning: This does not check for certain illegal sizes
begin
DOSIZE(NOSIZE, $5); \Byte or long (is implied)
OPSIZE:= BYTE; \Force to .B in case #<xxx>
DOEA($0801);
GETCOMMA;
if MODEFLAG=$0001 then
OPCODE:= OPCODE ! EA <<9 \Dynamic case -- get data register
else OPCODE:= OPCODE & $FEFF ! $0800; \Static case -- clear bit #8 & set 11
DOEA($01FD);
OPCODE:= OPCODE ! EA;
GENINSTR;
end; \DOBCHG
proc DOASL;
int VAL;
begin
DOSIZE(DEFOPSIZE, $7);
SKIPTAB;
if CHAR=^# then \ASL._ #<1..8>,Dn
begin
GETCHX; \Skip the "#"
VAL:= EXPRESSION;
if VAL>8 ! VAL<1 then ERROR("OVERFLOW");
OPCODE:= OPCODE ! (VAL&$7) <<9 ! (OPSIZE-1) <<6;
GETCOMMA;
DOEA($0001); \Get Dn
end
else begin
DOEA($01FD);
if MODEFLAG=$0001 then
begin \ASL._ Dn,Dn
OPCODE:= OPCODE ! EA <<9 ! (OPSIZE-1) <<6 ! $20;
GETCOMMA;
DOEA($0001); \Get Dn
end
else begin \ASL.W <ea>
if OPSIZE#WORD then ERROR("SIZE");
OPCODE:= OPCODE ! $C0;
end;
end;
OPCODE:= OPCODE ! EA;
GENINSTR;
end; \DOASL
proc DOBCC; \Bcc._ <label>
int VAL;
begin
DOSIZE(LONG, $C); \Long or short
SKIPTAB;
VAL:= EXPRESSION -(PC +2); \Get relative displacement
if OPSIZE=SHORT then
begin
if VAL=0 ! VAL>126 ! VAL<-126 then ERROR("OVERFLOW");
OPCODE:= OPCODE ! (VAL &$FF);
end
else begin \Long
if VAL>32764 ! VAL<-32764 then ERROR("OVERFLOW");
EXTLEN:=1;
EXTWD(0):= VAL;
end;
GENINSTR;
end; \DOBCC
proc DOADDQ; \ADDQ._ #<1..8>,<ea>
int VAL;
begin
DOSIZE(DEFOPSIZE, $7);
OPCODE:= OPCODE ! (OPSIZE-1) <<6;
SKIPTAB;
if CHAR#^# then ERROR("SYNTAX");
GETCHX;
VAL:= EXPRESSION;
if VAL>8 ! VAL<1 then ERROR("OVERFLOW");
OPCODE:= OPCODE ! (VAL&$7) <<9;
GETCOMMA;
DOEA($01FF);
OPCODE:= OPCODE ! EA;
GENINSTR;
end; \DOADDQ
proc DOMOVQ; \MOVEQ.L #<xxx>,Dn
int VAL;
begin
DOSIZE(LONG, $4);
SKIPTAB;
if CHAR#^# then ERROR("SYNTAX");
GETCHX;
VAL:= EXPRESSION;
if VAL>127 ! VAL<-128 then ERROR("OVERFLOW");
OPCODE:= OPCODE ! (VAL&$FF);
GETCOMMA;
DOEA($0001);
OPCODE:= OPCODE ! EA <<9;
GENINSTR;
end; \DOMOVQ
proc DOABCD; \ABCD.B Dy,Dx ABCD.B -(Ay),-(Ax)
begin
DOSIZE(BYTE, $1);
DOEA($0011);
OPCODE:= OPCODE ! (EA&$7) ! (if MODEFLAG = $0010 then $8 else $0);
GETCOMMA;
DOEA(MODEFLAG); \Must be the same mode
OPCODE:= OPCODE ! (EA&$7) <<9;
GENINSTR;
end; \DOABCD
proc DOADDX; \ADDX._ Dy,Dx ADDX._ -(Ay),-(Ax)
begin
DOSIZE(DEFOPSIZE, $7);
DOEA($0011);
OPCODE:= OPCODE ! (EA&$7) ! (if MODEFLAG = $0010 then $8 else $0);
GETCOMMA;
DOEA(MODEFLAG); \Must be the same mode
OPCODE:= OPCODE ! (EA&$7) <<9 ! (OPSIZE-1) <<6;
GENINSTR;
end; \DOADDX
proc DODBCC; \DBcc.W Dn,<label>
int VAL;
begin
DOSIZE(WORD, $2);
DOEA($0001);
OPCODE:= OPCODE ! EA;
GETCOMMA;
VAL:= EXPRESSION -(PC +2); \Get relative displacement
if VAL>32764 ! VAL<-32764 then ERROR("OVERFLOW");
EXTLEN:=1;
EXTWD(0):= VAL;
GENINSTR;
end; \DODBCC
proc DOSCC; \Scc.B <ea>
begin
DOSIZE(BYTE, $1);
DOEA($01FD);
OPCODE:= OPCODE ! EA;
GENINSTR;
end; \DOSCC
\----------------------------- PSEUDO OPS -----------------------------
proc DOORG; \ORG EXPRESSION
begin
DOSIZE(NOSIZE, $0);
SKIPTAB;
PC:= EXPRESSION; \Set PC to new value
LABEL:= PC;
\If there is a label then update its value to the new PC
if LABINX # -1 then
begin
SYMVAL(LABINX):= LABEL;
SYMTYPE(LABINX):=$01; \indicate changeable label (phase)
end;
if PASS = 2 then [CHOUT(OBJDEV, ^@); HEXOUT(OBJDEV, PC)];
end; \DOORG
proc DOEQU; \LABEL EQU EXPRESSION
int I;
begin
DOSIZE(NOSIZE, $0);
SKIPTAB;
LABEL:= EXPRESSION; \Set label to new value
if LABINX # -1 then
begin
SYMVAL(LABINX):= LABEL;
SYMTYPE(LABINX):=$01; \Indicate changable label
end
else ERROR("LABEL"); \Must have label
end; \DOEQU
proc DONOLIST; \NOLIST
begin
DOSIZE(NOSIZE, $0);
LISTON:=false;
end; \DONOLIST
proc DOLIST; \LIST
begin
DOSIZE(NOSIZE, $0);
LISTON:=true;
end; \DOLIST
proc DOPAGE; \PAGE
begin
DOSIZE(NOSIZE, $0);
if PASS=2 & LISTON then CHOUT(LISTDEV, FF);
end; \DOPAGE
proc DODC; \DC._ EXPRESSION, EXPRESSION, ..., EXPRESSION
int VAL;
begin
DOSIZE(DEFOPSIZE, $7);
if OPSIZE # BYTE then DOSYNC;
loop begin
SKIPTAB;
VAL:= EXPRESSION;
case OPSIZE of
BYTE: GEN_B(VAL);
WORD: GEN_W(VAL);
LONG: begin
GEN_W(SWAPWD(VAL)); \High word
GEN_W(VAL); \Low word
end
other;
if CHAR=^, then GETCHX else quit;
end;
end; \DODC
proc DODS; \DS._ EXPRESSION
int VAL, TBL;
begin
DOSIZE(DEFOPSIZE, $7);
if OPSIZE # BYTE then DOSYNC;
SKIPTAB;
VAL:= EXPRESSION;
TBL:= [0, 1, 2, 4]; \Number of bytes for each size
if (PC&1)#0 & OPSIZE>=WORD then \If we're out of sync and word or long
begin \ then leave space for a sync byte
PC:=PC +1;
LABEL:= PC;
\If there is a label then update value to new PC:
if LABINX # -1 then
begin
SYMVAL(LABINX):= LABEL;
SYMTYPE(LABINX):=$01; \Indicate changeable label (phase)
end;
end;
PC:= PC + VAL *TBL(OPSIZE);
if PASS = 2 then [CHOUT(OBJDEV, ^@); HEXOUT(OBJDEV, PC)];
end; \DODS
proc DODCB; \DCB._ TIMES, VAL
\Define constant block. I.e. define "TIMES" many constants of value "VAL"
int TIMES, VAL, I;
begin
DOSIZE(DEFOPSIZE, $7);
if OPSIZE # BYTE then DOSYNC;
SKIPTAB;
TIMES:= EXPRESSION;
GETCOMMA;
VAL:= EXPRESSION;
for I:=1,TIMES do
begin
case OPSIZE of
BYTE: GEN_B(VAL);
WORD: GEN_W(VAL);
LONG: begin
GEN_W(SWAPWD(VAL)); \HIGH WORD
GEN_W(VAL); \LOW WORD
end
other;
end;
end; \DODCB
proc DOASCII; \ASCII "STRING"
int QUOTE;
\If the string contains an apostrophy, use double quotes.
\If the string contains double quotes, use single quotes.
\If a carriage return terminates the string, it is included as part of the string.
begin
DOSIZE(NOSIZE, $0);
SKIPTAB;
QUOTE:= CHAR;
if QUOTE#^" & QUOTE#^' then ERROR("QUOTE EXPECTED")
else loop begin
CHAR:= LINEBUF(LINEINX); \(don't shift to upper case)
LINEINX:= LINEINX +1;
if CHAR = QUOTE then [GETCHX; quit]; \skip it and exit
GEN_B(CHAR);
if CHAR = CR then quit;
end;
end; \DOASCII
proc DOIF; \IF EXPRESSION
\Warning: A label on this line is already entered in symbol table regardless
\ of whether this evaluates to true or false.
begin
DOSIZE(NOSIZE, $0);
SKIPTAB;
ASMON:= EXPRESSION;
LABEL:= ASMON; \Show value of expression in label field
end; \DOIF
proc DOELSE; \ELSE
begin
DOSIZE(NOSIZE, $0);
ASMON:= not ASMON;
end; \DOELSE
proc DOENDIF; \ENDIF
begin
DOSIZE(NOSIZE, $0);
ASMON:= true;
end; \DOENDIF
proc DOINCL; \"INCLUDE" pseudo op
int BLK; \Array: for SCAN
addr NAME; \Array: holds file name
proc GETNAME; \Get name & unit of include file
int I;
begin
INUNIT:= ADDR(INUNT); \Get the default input unit number
NAME(8):= ^6; NAME(9):= ^8; NAME(10):= ^K; \Default extension
if CHAR>=^0 & CHAR<=^9 then
begin \We have an explicit unit number
INUNIT:= CHAR & $0F;
GETCHX; \Skip unit number
if CHAR#^: then ERROR("^":^" EXPECTED");
GETCHX; \Skip the colon
end;
GETIDENT; \Get the file name (must be 8 chars)
for I:=0,7 do NAME(I):= IDENT(I);
if CHAR=^. then
begin \We have an explicit extension
GETCHX; \Skip the "."
GETIDENT;
for I:=0,2 do NAME(I+8):= IDENT(I);
end;
end; \GETNAME
proc STROUT(STR,SIZE); \Output string to TV
addr STR;
int SIZE;
int I;
begin
for I:=0, SIZE-1 do
CHOUT(TV, STR(I));
end; \STROUT
begin \DOINCL
NAME:= RESERVE(11);
BLK:= RESERVE(2 *INTSIZE);
if INCLLEV > 7 then
[ERROR("INCLUDES NESTED MORE THAN 8 LEVELS"); return];
SKIPTAB;
INCLNEST(INCLLEV, 0):= FBLK; \Save current file parameters
INCLNEST(INCLLEV, 1):= LBLK;
INCLNEST(INCLLEV, 2):= INBUFINX;
INCLNEST(INCLLEV, 3):= INUNIT;
LINECTR:= 0; \Count lines from start of INCLUDE file
INCLNEST(INCLLEV, 4):= LINECTR;
INCLLEV:= INCLLEV +1; \Nest one level deeper
INBUFINX:= INBUFSIZE; \Indicate that new buffer is empty
GETNAME; \Get the file's name
SCAN(INUNIT, BLK, NAME); \Get its first and last blocks
FBLK:= BLK(0);
LBLK:= BLK(1);
\Show the name of the inculded file:
TEXT(TV,"INCLUDING: ");
INTOUT(TV,INUNIT); CHOUT(TV,^:);
STROUT(NAME,8); CHOUT(TV,^.); STROUT(NAME+8,3); CRLF(TV);
end; \DOINCL
proc DOEND;
begin
DOSIZE(NOSIZE, $0);
ENDFOUND:= true;
end; \DOEND
\----------------------------------------------------------------------
proc ASMLINE; \Assemble a line of source code
int I;
begin
EXTLEN:= 0; \Assume no opcode extension words
GENINX:= 0;
LABEL:= PC; \Assume not EQU, ORG
LABINX:= -1; \Assume no label
MNTYPE:= -1; \Indicate no mnemonic (may be a comment line)
GETCH; \Get first character (don't skip tabs)
if CHAR>=^A & CHAR<=^Z then \We have a label
begin
GETIDENT;
if PASS = 1 then
begin
LABINX:= SYMNUM; \Save index in case EQU or ORG
INSERT(LAB, PC, 0);
end
else begin \Pass 2 -- check for phase error
LABINX:= LOOKUP(LAB); \If the label's changed & not EQU...
if SYMVAL(LABINX)#PC & (SYMTYPE(LABINX) & $01) =0 then
ERROR("PHASE");
DOXREF(LABINX,true);
end;
if CHAR=^: then GETCH; \Ignore colon
end;
SKIPTAB;
if CHAR>=^A & CHAR<=^Z then \We have a mnemonic
begin
GETIDENT;
I:= LOOKUP(MN);
if I= -1 then ERROR("MNEMONIC")
else begin
OPCODE:= SYMVAL(I);
MNTYPE:= SYMTYPE(I);
if MNTYPE>=NOP & MNTYPE<=SCC then DOSYNC;
case MNTYPE of
NOP: DONOP;
JMP: DOJMP;
MOVE: DOMOV;
MOVEA: DOMOVA;
MOVEP: DOMOVP;
MOVEM: DOMOVM;
CLR: DOCLR;
EXT: DOEXT;
SWP: DOSWAP;
ADDI: DOADDI;
NBCD: DONBCD;
EOR: DOEOR;
EXG: DOEXG;
LEA: DOLEA;
LINK: DOLINK;
STOP: DOSTOP;
TRAP: DOTRAP;
UNLK: DOUNLK;
ADD: DOADD;
CMP: DOCMP;
CMPM: DOCMPM;
ADDA: DOADDA;
AND: DOAND;
ANDI: DOANDI;
CHK: DOCHK;
BCHG: DOBCHG;
ASL: DOASL;
BCC: DOBCC;
ADDQ: DOADDQ;
MOVEQ: DOMOVQ;
ABCD: DOABCD;
ADDX: DOADDX;
DBCC: DODBCC;
SCC: DOSCC;
ORG: DOORG; \Pseudo-ops
EQU: DOEQU;
NOLIST:DONOLIST;
LIST: DOLIST;
PAGE: DOPAGE;
DC: DODC;
DS: DODS;
DCB: DODCB;
ASCII: DOASCII;
IF: DOIF;
ELSE: DOELSE;
ENDIF: DOENDIF;
INCLUDE: DOINCL;
END: DOEND
other ERROR("INTERNAL");
end;
end;
\The rest of the line must be blank (or a comment)
\(Note: "*" cannot be used as a comment delimiter because of expressions.
\ E.g: CLR.W FROG+2 *10 hops)
SKIPTAB;
case CHAR of
CR, ^; : []
other ERROR("SYNTAX");
end; \ASMLINE
\----------------------------------------------------------------------
proc INIT; \Main initialization
int I, TBL;
proc LOADSYM(TBL, CLASS); \Load symbol table
int TBL, CLASS;
int I, SLINEBUF;
begin
SLINEBUF:= LINEBUF; \Save line buffer
I:=0;
loop begin
LINEBUF:= TBL(I); \Set up arguments for GETIDENT
if LINEBUF = -1 then quit;
LINEINX:= 0;
GETCH;
GETIDENT;
INSERT(CLASS, TBL(I+1), TBL(I+2));
I:=I+3;
end;
LINEBUF:= SLINEBUF;
end; \LOADSYM
begin \INIT
loop begin \(Allows saving defaults with a CTRL-P)
TEXT(TV,"CHANGE DEFAULTS (N/Y)? ");
if (CHIN(KB)!$20)#^y then quit;
TEXT(TV,"DEVICE NUMBERS (OBJECT, LISTING)? ");
DEFAULT(0):= INTIN(KB); DEFAULT(1):= INTIN(KB);
TEXT(TV,"DO CROSS REFERENCE (N/Y)? ");
OPENI(KB);
DEFAULT(2):= (CHIN(KB)!$20) = ^y;
OPENI(KB);
end;
OBJDEV:= DEFAULT(0); LISTDEV:= DEFAULT(1);
OPENO(OBJDEV); OPENO(LISTDEV);
XREFON:= DEFAULT(2);
\Initialize some other stuff:
CRLF(TV);
ERRCNT:= 0;
SYMNUM:= 0;
XREFNUM:= 1; \Note: "0" is used to mark empty (neg nos are used for labels)
for I:= 0,255 do HASHTBL(I):= -1; \Indicate empty
if XREFON then for I:= 0,SYMSIZE-1 do SYMXREF(I):= 0; \Indicate empty
\Load reserved names into symbol table:
\ NAME MODEFLAG EA
TBL:= [ "D0", $0001, $0,
"D1", $0001, $1,
"D2", $0001, $2,
"D3", $0001, $3,
"D4", $0001, $4,
"D5", $0001, $5,
"D6", $0001, $6,
"D7", $0001, $7,
"A0", $0002, $8,
"A1", $0002, $9,
"A2", $0002, $A,
"A3", $0002, $B,
"A4", $0002, $C,
"A5", $0002, $D,
"A6", $0002, $E,
"A7", $0002, $F,
"SP", $0002, $F,
\(EA must be distinguishable from registers)
"CCR", $4000, $3C,
"SR", $2000, $7C,
"USP", $1000, $10,
\These are reserved for future use:
"SSP", $8000, $10,
"PC", $8000, $10,
-1];
LOADSYM(TBL,RES);
\Load mnemonics into symbol table:
\ NAME OPCODE TYPE
TBL:= [ "NOP", $4E71, NOP,
"RESET", $4E70, NOP,
"RTE", $4E73, NOP,
"RTR", $4E77, NOP,
"RTS", $4E75, NOP,
"TRAPV", $4E76, NOP,
"JMP", $4EC0, JMP,
"JSR", $4E80, JMP,
"PEA", $4840, JMP,
"MOVE", $0000, MOVE,
"MOVEA", $0000, MOVEA,
"MOVEP", $0108, MOVEP,
"MOVEM", $4880, MOVEM,
"CLR", $4200, CLR,
"NEG", $4400, CLR,
"NEGX", $4000, CLR,
"NOT", $4600, CLR,
"TST", $4A00, CLR,
"EXT", $4880, EXT,
"SWAP", $4840, SWP,
"ADDI", $0600, ADDI,
"SUBI", $0400, ADDI,
"CMPI", $0C00, ADDI,
"NBCD", $4800, NBCD,
"TAS", $4AC0, NBCD,
"EOR", $B100, EOR,
"EXG", $C100, EXG,
"LEA", $41C0, LEA,
"LINK", $4E50, LINK,
"STOP", $4E72, STOP,
"TRAP", $4E40, TRAP,
"UNLK", $4E58, UNLK,
"ADD", $D000, ADD,
"SUB", $9000, ADD,
"CMP", $B000, CMP,
"CMPM", $B108, CMPM,
"ADDA", $D0C0, ADDA,
"CMPA", $B0C0, ADDA,
"SUBA", $90C0, ADDA,
"AND", $C000, AND,
"OR", $8000, AND,
"ANDI", $0200, ANDI,
"EORI", $0A00, ANDI,
"ORI", $0000, ANDI,
"CHK", $4180, CHK,
"DIVS", $81C0, CHK,
"DIVU", $80C0, CHK,
"MULS", $C1C0, CHK,
"MULU", $C0C0, CHK,
"BCHG", $0140, BCHG, \(Bit #8 must be set & 11 clear
"BCLR", $0180, BCHG, \ for BCHG types)
"BSET", $01C0, BCHG,
"BTST", $0100, BCHG,
"ASL", $E100, ASL,
"ASR", $E000, ASL,
"LSL", $E108, ASL,
"LSR", $E008, ASL,
"ROL", $E118, ASL,
"ROR", $E018, ASL,
"ROXL", $E110, ASL,
"ROXR", $E010, ASL,
"BRA", $6000, BCC,
"BSR", $6100, BCC,
"BHI", $6200, BCC,
"BLS", $6300, BCC,
"BCC", $6400, BCC,
"BHS", $6400, BCC,
"BCS", $6500, BCC,
"BLO", $6500, BCC,
"BNE", $6600, BCC,
"BEQ", $6700, BCC,
"BVC", $6800, BCC,
"BVS", $6900, BCC,
"BPL", $6A00, BCC,
"BMI", $6B00, BCC,
"BGE", $6C00, BCC,
"BLT", $6D00, BCC,
"BGT", $6E00, BCC,
"BLE", $6F00, BCC,
"ADDQ", $5000, ADDQ,
"SUBQ", $5100, ADDQ,
"MOVEQ", $7000, MOVEQ,
"ABCD", $C100, ABCD,
"SBCD", $8100, ABCD,
"ADDX", $D100, ADDX,
"SUBX", $9100, ADDX,
"DBT", $50C8, DBCC,
"DBF", $51C8, DBCC,
"DBHI", $52C8, DBCC,
"DBLS", $53C8, DBCC,
"DBCC", $54C8, DBCC,
"DBHS", $54C8, DBCC,
"DBCS", $55C8, DBCC,
"DBLO", $55C8, DBCC,
"DBNE", $56C8, DBCC,
"DBEQ", $57C8, DBCC,
"DBVC", $58C8, DBCC,
"DBVS", $59C8, DBCC,
"DBPL", $5AC8, DBCC,
"DBMI", $5BC8, DBCC,
"DBGE", $5CC8, DBCC,
"DBLT", $5DC8, DBCC,
"DBGT", $5EC8, DBCC,
"DBLE", $5FC8, DBCC,
"ST", $50C0, SCC,
"SF", $51C0, SCC,
"SHI", $52C0, SCC,
"SLS", $53C0, SCC,
"SCC", $54C0, SCC,
"SHS", $54C0, SCC,
"SCS", $55C0, SCC,
"SLO", $55C0, SCC,
"SNE", $56C0, SCC,
"SEQ", $57C0, SCC,
"SVC", $58C0, SCC,
"SVS", $59C0, SCC,
"SPL", $5AC0, SCC,
"SMI", $5BC0, SCC,
"SGE", $5CC0, SCC,
"SLT", $5DC0, SCC,
"SGT", $5EC0, SCC,
"SLE", $5FC0, SCC,
\PSEUDO-OPS:
"ORG", $0000, ORG,
"EQU", $0000, EQU,
"NOLIST", $0000, NOLIST,
"LIST", $0000, LIST,
"PAGE", $0000, PAGE,
"DC", $0000, DC,
"DS", $0000, DS,
"DCB", $0000, DCB,
"ASCII", $0000, ASCII,
"IF", $0000, IF,
"ELSE", $0000, ELSE,
"ENDIF", $0000, ENDIF,
"INCLUDE", $0000, INCLUDE,
"END", $0000, END,
-1];
LOADSYM(TBL,MN);
end; \INIT
\----------------------------------------------------------------------
begin \MAIN
LINEBUF:= RESERVE(LINESIZE);
IDENT:= RESERVE(SIGCHAR);
SYMTBL:= RESERVE(SIGCHAR *SYMSIZE);
SYMCLASS:= RESERVE(SYMSIZE);
\(SYMCLASS must follow SYMTBL for QUICKSORT to work -- insures unique entries)
SYMVAL:= RESERVE(SYMSIZE *INTSIZE);
SYMTYPE:= RESERVE(SYMSIZE);
SYMNEXT:= RESERVE(SYMSIZE *INTSIZE);
HASHTBL:= RESERVE(256 *INTSIZE); \Hash table
EXTWD:= RESERVE(4 *INTSIZE);
GENBUF:= RESERVE(GENSIZE);
SYMINX:= SYMNEXT; \Use same array space
SYMXREF:= RESERVE(SYMSIZE *INTSIZE);
XREFPC:= RESERVE(XREFSIZE *INTSIZE);
XREFNEXT:= RESERVE(XREFSIZE *INTSIZE);
INCLNEST:= RESERVE(8 *INTSIZE);
for II:= 0,7 do INCLNEST(II):= RESERVE(5 *INTSIZE);
DEFAULT:= [3, 7, false]; \Defaults (OBJ, LIST, XREF)
ADDR:= 0;
TEXT(TV,"-- ASM68K, V1.0x10 --
");
INIT;
for PASS:=1,2 do
begin
TEXT(TV,"PASS "); INTOUT(TV,PASS); TEXT(TV,"..."); CRLF(TV);
OPENIN; \Initialize input
PC:= 0; \Set default PC
ENDFOUND:= false;
LISTON:= true; \Default is "LIST"
ASMON:= true; \Turn on assembler (IF)
LINECTR:= 0;
repeat begin
ERRTOLD:= false; \No errors reported yet for this line
GETLINE;
if ENDFOUND then \Assume "END" if EOF was found
ERROR("^"END^" EXPECTED");
if ASMON then ASMLINE
else begin \Conditional assy is off -- look
GETCH; \ for an ENDIF or ELSE
if CHAR>=^A & CHAR<=^Z then GETIDENT; \Skip label
SKIPTAB;
if CHAR>=^A & CHAR<=^Z then
begin
GETIDENT; \Get mnemonic
II:= LOOKUP(MN);
if II # -1 then
begin
MNTYPE:= SYMTYPE(II);
case SYMTYPE(II) of
ELSE: DOELSE;
ENDIF: DOENDIF
other;
end;
end;
end;
if PASS=2 & LISTON & LISTDEV#NULLDEV then
LISTOUT(LISTDEV);
end;
until ENDFOUND;
end;
if XREFON then XREFOUT;
CRLF(LISTDEV);
TEXT(LISTDEV,"ERRORS DETECTED: "); INTOUT(LISTDEV,ERRCNT); CRLF(LISTDEV);
if LISTDEV#TV then
begin
CRLF(TV);
TEXT(TV,"ERRORS DETECTED: "); INTOUT(TV,ERRCNT); CRLF(TV);
end;
if ERRCNT>0 then CHOUT(TV,BEL);
CLOSE(LISTDEV);
CLOSE(OBJDEV);
end; \MAIN
TECTED: "); INTOUT(TV,E